import React, { Component } from 'react'

// 用于创建列表的方法，可以不关注
function getList(pageNo = 1) {
  console.log('有请求数据')
  let count = (pageNo - 1) * 10
  const list = []
  for (let i = 0; i < 10; i++) {
    count += 1
    if (count > 220) {
      break
    }
    list.push({
      text: `这是第${count}条数据`,
      id: 'data' + count,
    })
  }
  return {
    total: 220,
    list,
  }
}

export default class Observer extends Component {
  constructor(props) {
    super(props)
    this.state = {
      pageNo: 0,
      list: [],
      loading: false, // 是否正在加载中
      listEnd: false, // 列表是否已经加载完成
    }

    this.cacheList = []
    // 观察者模式需要的元素
    this.listWrap = React.createRef()
    this.firstItem = React.createRef()
    this.lastItem = React.createRef()
  }
  /**
   * 获取列表
   * @returns
   */
  getList(pageNo, direction = 'btm') {
    if (this.state.loading) {
      return
    }
    this.setState({
      loading: true,
    })
    const currPageMax = pageNo * 10 // 当前页最大条目
    // 如果本地缓存中已有指定列表数据，直接渲染本地列表数据
    console.log(pageNo, direction, currPageMax, this.cacheList.length)

    if (currPageMax <= this.cacheList.length) {
      const start = currPageMax - (direction === 'top' ? 20 : 10)
      const end = direction === 'top' ? currPageMax : currPageMax + 10
      this.setState({
        pageNo,
        loading: false,
        list: this.cacheList.slice(start, end),
      })
    } else {
      // 如果列表已经到了最后，不再请求接口
      if (this.state.listEnd) {
        console.log('############列表完了############')
        this.setState({
          loading: false,
        })
        return
      }
      console.log('继续获取数据', pageNo)
      // 模拟接口延迟
      setTimeout(() => {
        const data = getList(pageNo)
        // 缓存当前列表数据
        this.cacheList.push(...data.list)
        console.log(this.cacheList, '$$$$$$$$$$')
        // 写入state
        this.setState({
          pageNo,
          // 脱离加载状态
          loading: false,
          // 判断当前列表是否已经加载完成
          listEnd: data.total <= this.cacheList.length,
          // 取缓存中的倒数20条数据
          list: this.cacheList.slice(-20),
        })
      }, Math.random() * 1000)
    }
  }
  /**
   * 页面初始化时，实例化一个IntersectionObserver对象
   * 使用实例化对象的observe方法来监听一个元素是否在可视区域
   * 通过实例化时传入的回调函数返回的数组来确认当前触发可视区域变化回调的元素是否是自己监听的元素
   * 通过判断当前监听的元素的isIntersecting值来判断元素是否已经进入可视区域
   * 如果进入可视区域，触发获取更多数据的请求。
   */
  componentDidMount() {
    // this.getList(this.state.pageNo)
    /**
     * 实例化观察对象的回调函数
     * @param {*} entries 一个数组，里面有我们需要观察的所有对象
     */
    const observerCallback = entries => {
      // 在这里检查是哪一个元素即将进入或离开视界
      // time：可见性发生变化的时间，是一个高精度时间戳，单位为毫秒
      // target：被观察的目标元素，是一个 DOM 节点对象
      // rootBounds：根元素的矩形区域的信息，getBoundingClientRect()方法的返回值，如果没有根元素（即直接相对于视口滚动），则返回null
      // boundingClientRect：目标元素的矩形区域的信息
      // intersectionRect：目标元素与视界（或根元素）的交叉区域的信息
      // intersectionRatio：目标元素的可见比例，即intersectionRect占boundingClientRect的比例，完全可见时为1，完全不可见时小于等于0
      for (const item of entries) {
        // 如果是底部的元素，而且已进入可见区域
        if (item.target.className.includes('last') && item.isIntersecting) {
          console.log('快到底了，开始加载数据')
          this.getList(this.state.pageNo + 1)
          return
        }

        if (item.target.className.includes('first') && item.isIntersecting) {
          console.log('快到顶了，开始加载数据')
          this.getList(this.state.pageNo - 1, 'top')
          return
        }
      }
    }
    // 配置项
    const options = {
      // root: null, // 不指定根元素（视界）
      root: this.listWrap.current,
      rootMargin: '150px 0px', // 扩展的视界，必须%或px
    }
    // 建立一个观察者对象 new IntersectionObserver(回调函数)
    this.observer = new IntersectionObserver(observerCallback, options)
    // 如果可用元素已经创建，监听这个元素的变化
    this.lastItem.current && this.observer.observe(this.lastItem.current)
    this.firstItem.current && this.observer.observe(this.firstItem.current)
  }
  render() {
    return (
      <div className="observer-page">
        <div ref={this.listWrap} className="list-wrap">
          <div className="list-item first" ref={this.firstItem}></div>
          {this.state.list.map(item => (
            <div className="list-item" key={item.id}>
              {item.text}
            </div>
          ))}
          <div className="list-item last" ref={this.lastItem}>
            {this.state.listEnd
              ? '哥也是有底线的'
              : this.state.loading
              ? '请稍候，正在加载数据...'
              : '加载完成，渲染中'}
          </div>
        </div>
      </div>
    )
  }
}
